/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package bgu.ppm.gtm.base;

import bgu.ppm.sim.api.mas.Model;
import bgu.sonar.util.collections.Pair;
import java.util.HashSet;

/**
 *
 * @author User
 */
public abstract class GameTheoryModel extends Model {

    private PlayerValueFunction utilityFunction;
    private PlayerValueFunction costFunction;
    private BestResponseFinder bestResponseFinder = null;
    private StrategyProfile currentStrategyProfile;

    @Override
    public final void initialize() {
        _initialize();
        currentStrategyProfile = getStrategyProfileBuilder().build();
        if (bestResponseFinder == null) {
            bestResponseFinder = new ExhaustiveBestResponseFinder(this);
        }
    }

    public abstract Strategies getStrategiesFor(int player);

    public PlayerValueFunction getCostFunction() {
        return costFunction;
    }

    public PlayerValueFunction getUtilityFunction() {
        return utilityFunction;
    }

    public BestResponseFinder getBestResponseFinder() {
        return bestResponseFinder;
    }

    protected abstract StrategyProfileBuilder getStrategyProfileBuilder();

    protected void setBestResponseFinder(BestResponseFinder bestResponseFinder) {
        this.bestResponseFinder = bestResponseFinder;
    }

    protected void setCostFunction(PlayerValueFunction costFunction) {
        this.costFunction = costFunction;
    }

    protected void setUtilityFunction(PlayerValueFunction utilityFunction) {
        this.utilityFunction = utilityFunction;
    }

    public StrategyProfile getCurrentStrategyProfile() {
        return currentStrategyProfile;
    }

    public void selectStrategy(int player, ImmutableStrategy strategy) {
//        System.out.println("Strategy: " + strategy + " selected by agent: " + player);
        currentStrategyProfile = getStrategyProfileBuilder().build(currentStrategyProfile, strategy);
    }

    public void removeStrategy(int player) {
        ImmutableStrategy ps = currentStrategyProfile.getSelectedStrategy(player);
        HashSet<ImmutableStrategy> ns = new HashSet<>();
        for (Pair<Integer, ImmutableStrategy> s : currentStrategyProfile) {
            if (s.getY() != ps) {
                ns.add(s.getY());
            }
        }

        currentStrategyProfile = getStrategyProfileBuilder().build(ns);
    }

    public boolean isBestResponce(int player, ImmutableStrategy strategy) {
        for (ImmutableStrategy s_ : getStrategiesFor(player)) {
            if (getUtilityFunction().calculate(player, s_, currentStrategyProfile) > getUtilityFunction().calculate(player, strategy, currentStrategyProfile)) {
                return false;
            }
        }

        return true;
    }

    public boolean isCurrentStrategyProfilePNE() {
        for (int i = 0; i < getNumberOfAgents(); i++) {
            ImmutableStrategy s = currentStrategyProfile.getSelectedStrategy(i);

            if (s == null) {
                throw new NotFullStrategyProfileException("Agent " + i + " doesn't choose a strategy. PNE check cannot be accomplished");
            }

            if (!isBestResponce(i, s)) {
                return false;
            }
        }

        return true;
    }

    protected abstract void _initialize();

    private static class NotFullStrategyProfileException extends RuntimeException {

        public NotFullStrategyProfileException(String message) {
            super(message);
        }

        public NotFullStrategyProfileException(String message, Throwable cause) {
            super(message, cause);
        }

        public NotFullStrategyProfileException(Throwable cause) {
            super(cause);
        }
    }
}
